package com.home.auto_navigation.ui;
 
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.openqa.selenium.remote.RemoteWebDriver;

import com.home.auto_navigation.common.Driver;
import com.home.auto_navigation.common.ShortWay;
import com.home.auto_navigation.common.SiteMap;
import com.home.auto_navigation.common.api.OpenedPageNotFoundException;
import com.home.auto_navigation.common.api.PageNotFoundException;
import com.home.auto_navigation.ui.controllers.Button;
import com.home.auto_navigation.ui.pages.HomePage;

public abstract class AbstractPageObject implements PageObject {

	private Driver driver = Driver.getInstance();
	public Map<Class<? extends AbstractPageObject>, Button> successors = null;
	private static Class<? extends AbstractPageObject> openedPage = HomePage.class;

	public List<Class<? extends AbstractPageObject>> getSuccessorKey(
			Map<Class<? extends AbstractPageObject>, Button> successorsPage) {
		List<Class<? extends AbstractPageObject>> list = new LinkedList<>();
		list.addAll(successorsPage.keySet());
		return list;
	}

	public void invoke() {
	    invoke(null, null);
	}
	
	/**
	 * necessary pages
	 * */
	public void invoke(Deque<Class<? extends AbstractPageObject>> necessaryPages){
	    invoke(necessaryPages, null);
	}
	/**
	 * business data
	 * */
	public void invoke(Map<String, String> businessData){
        invoke(null, businessData);
    }
	/**
	 * web url
	 * */
	public void invoke(String url) {
        driver.openUrl(url);
    }

    @SuppressWarnings("unchecked")
    public void invoke(Deque<Class<? extends AbstractPageObject>> necessaryPages,
            Map<String, String> businessData) {
        Map<Class<? extends AbstractPageObject>, List<Class<? extends AbstractPageObject>>> siteMap = SiteMap.getInstance().getMap();
        try {
            if (!siteMap.containsKey(this.getClass()))
                throw new PageNotFoundException("this page: "
                        + this.getClass().getSimpleName()
                        + " not exist in siteMap");
            Class<? extends AbstractPageObject> startPage = getOpenedPage(siteMap);
            //TODO check if startPage!=null
            List<Class<? extends AbstractPageObject>> way = null;
            if (necessaryPages != null) {
                do {
                    if (way == null) {
                        way = ShortWay.getShortWay(startPage, necessaryPages.getFirst());
                    } else {
                        way.addAll(ShortWay.getShortWay(startPage, necessaryPages.getFirst()));
                    }
                    ((LinkedList<Class<? extends AbstractPageObject>>) way).removeLast();
                    startPage = necessaryPages.getFirst();
                    necessaryPages.removeFirst();
                } while (necessaryPages.size() > 0);
                way.addAll(ShortWay.getShortWay(startPage, this.getClass()));
            } else if (necessaryPages == null) {
                way = ShortWay.getShortWay(startPage, this.getClass());
            }
            if (way != null)
                passWay((Deque<Class<? extends AbstractPageObject>>) way, businessData);
        } catch (PageNotFoundException e) {
            e.printStackTrace();
        }
    }
    
	private void passWay(Deque<Class<? extends AbstractPageObject>> way,
			Map<String, String> businessData) {
		do {
			try {
				AbstractPageObject currentPage = way.getFirst().newInstance();
	            way.removeFirst();
				currentPage.invokeSuccessor(businessData, way.getFirst());
				currentPage.successors.get(way.getFirst()).click();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		} while (way.size() > 1);
		openedPage = way.getFirst();
	}

	private Class<? extends AbstractPageObject> getOpenedPage(
			Map<Class<? extends AbstractPageObject>, List<Class<? extends AbstractPageObject>>> siteMap) {
		try {
			if (openedPage.newInstance().isExist())
				return openedPage;
			driver.implicitlyWait(1, TimeUnit.MILLISECONDS);
			LinkedList<Class<? extends AbstractPageObject>> list = new LinkedList<>();
			list.addAll(siteMap.keySet());
			for (Class<? extends AbstractPageObject> startPage : list) {
				if (startPage.newInstance().isExist()) {
					driver.implicitlyWait(5, TimeUnit.SECONDS);
					return startPage;
				}
			}
			throw new OpenedPageNotFoundException(
					"opened page not found, please check your uniqueElement");
		} catch (OpenedPageNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return null;
	}

	protected RemoteWebDriver getDriver() {
		return driver.getDriver();
	}

	public abstract boolean isExist();

	public abstract void invokeSuccessor(Map<String, String> businessData,
			Class<? extends AbstractPageObject> page);
}
